package radar24g

import (
	"fmt"
	"github.com/tarm/serial"
	"io"
	"strconv"
	"time"
)

type Radar24G struct {
	device           string
	SerialPort       *serial.Port
	DataGroupMaxSize int
	DataGroup        []SensorData
}

type SensorData struct {
	SensorStatus string
	Signal       int
}

// New 初始化串口雷达对象
func (r *Radar24G) New(device string, MaxSize int) *Radar24G {
	return &Radar24G{
		device:           device,
		SerialPort:       &serial.Port{},
		DataGroupMaxSize: MaxSize,
		DataGroup:        []SensorData{},
	}
}

// Size 获取当前雷达数据存储组大小
func (r *Radar24G) Size() int {
	return len(r.DataGroup)
}

// IsFull 判断雷达数据存储组是否已满
func (r *Radar24G) IsFull() bool {
	return r.Size() == r.DataGroupMaxSize
}

// IsEmpty 判断雷达数据存储组是否为空
func (r *Radar24G) IsEmpty() bool {
	return r.Size() == 0
}

// Push 向雷达对象中数据存储组添加雷达读数对象数据
func (r *Radar24G) Push(val SensorData) {
	if r.IsFull() {
		r.DataGroup = append(r.DataGroup, val)
		r.DataGroup = r.DataGroup[1:len(r.DataGroup)]
		return
	}
	r.DataGroup = append(r.DataGroup, val)
	return
}

// OpenPort 启动串口
func (r *Radar24G) OpenPort() error {
	config := &serial.Config{
		Name:        r.device,
		Baud:        115200,
		ReadTimeout: 100 * time.Millisecond,
	}

	SerialPort, err := serial.OpenPort(config)
	if err != nil {
		return fmt.Errorf("serial port open failed: %s", err)
	}
	r.SerialPort = SerialPort
	return nil
}

// GetRawData 获取串口雷达读数原始数据
func (r *Radar24G) GetRawData() ([]byte, error) {

	buf := make([]byte, 8)
	rawData := make([]byte, 0)

	//执行两次清理串口已存在的数据
	err := r.SerialPort.Flush()
	if err != nil {
		return nil, fmt.Errorf("radar rawdata perflush failed: %s", err)
	}
	err = r.SerialPort.Flush()
	if err != nil {
		return nil, fmt.Errorf("radar rawdata perflush failed: %s", err)
	}

	//树莓派zero上一次只能读取到8个字节，所以读取两次然后拼接在一起
	for i := 0; i < 2; i++ {
		n, err := r.SerialPort.Read(buf)
		if err != nil {
			if err != io.EOF {
				return nil, fmt.Errorf("radar rawdata read faild: %s", err)
			}
			if err == io.EOF {
				//log.Fatalf("EOF error")
				rawData = append(rawData, "emp"...)
				err = r.SerialPort.Flush()
				if err != nil {
					return nil, fmt.Errorf("radar rawdata perflush failed: %s", err)
				}
				return rawData, nil
			}
		}
		rawData = append(rawData, buf[:n]...)
	}

	err = r.SerialPort.Flush()
	if err != nil {
		return nil, fmt.Errorf("radar rawdata perflush failed: %s", err)
	}

	return rawData, nil
}

// RawDataProcess 将从串口获取的雷达原始数据反序列化到雷达读数对象
func (r *Radar24G) RawDataProcess(RawData []byte) (*SensorData, error) {
	SensorData := new(SensorData)
	//判断状态是否为occ或mov或emp
	if string(RawData[:3]) != "occ" && string(RawData[:3]) != "mov" && string(RawData[:3]) != "emp" {
		return nil, fmt.Errorf("sensor stat value of RawData is not one of 'occ moc emp': %s", string(RawData[:3]))
	}
	//判断状态为emp则直接返回数据
	if string(RawData[:3]) == "emp" {
		SensorData.SensorStatus = string(RawData[:3])
		SensorData.Signal = 0
		return SensorData, nil
	}

	SensorData.SensorStatus = string(RawData[:3])
	//下面的字符串转换为数字可能由于字符串内含有空格等特殊字符导致转换失败出现panic，需要对字符串进行优化处理
	//radar24g Raw data process failed: signal value convert faild: strconv.Atoi: parsing " 1529": invalid syntax, sensordata is occ, rawdata is "occ, 10 1529\r\n"
	var tag int
	for i := 1; i <= len(RawData); i++ {
		//fmt.Printf("Raw: %q, len: %d\n", RawData[:],len(RawData))
		//fmt.Printf("Raw len-%d: %q\n", i, RawData[len(RawData)-i])
		if string(RawData[len(RawData)-i]) == " " {
			tag = len(RawData) - i
			//fmt.Printf("tag: %d\n", tag)
			break
		}
	}

	if tag+1 >= len(RawData)-2 {
		return nil, fmt.Errorf("data slice bounds out of range: %d:%d", tag+1, len(RawData)-2)
	}

	signal, err := strconv.Atoi(string(RawData[tag+1 : len(RawData)-2]))
	if err != nil {
		return SensorData, fmt.Errorf("signal value convert faild: %s, sensordata is %s, rawdata is %q", err, SensorData.SensorStatus, RawData[:])
	}
	SensorData.Signal = signal
	return SensorData, nil
}

func (r *Radar24G) Read() (*SensorData, error) {
	RawData, err := r.GetRawData()
	if err != nil {
		return nil, fmt.Errorf("radar24g Read raw data failed: %s", err)
	}
	data, err := r.RawDataProcess(RawData)
	if err != nil {
		return nil, fmt.Errorf("radar24g Raw data process failed: %s", err)
	}
	return data, nil
}
